﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace PControl
{
    /// <summary>
    /// 数字显示控件
    /// </summary>
    public partial class DigitNumber : UserControl
    {
        /// <summary>
        /// 控件待显示的值
        /// </summary>
        private float number = 0.0f;
        /// <summary>
        /// 控件显示字符长度
        /// </summary>
        private int length = 3;
        /// <summary>
        /// 控件显示字符
        /// </summary>
        private String dataStr = "0.0";
        /// <summary>
        /// 控件显示数字颜色
        /// </summary>
        private Color dialColor = Color.Blue;
        /// <summary>
        /// 字符串格式化匹配类型
        /// </summary>
        private String formatStr = "{0:0.0}";
        /// <summary>
        /// 显示数字的精度
        /// </summary>
        private int precision = 1;
        /// <summary>
        /// 修改控件之前的尺寸
        /// </summary>
        private Size preSize;
        /// <summary>
        /// 控件显示宽度
        /// </summary>
        private int width;
        /// <summary>
        /// 控件边框样式
        /// </summary>
        private BorderStyle borderStyle = BorderStyle.None;

        private int styleCompensate=0;

        /// <summary>
        /// 获取或设置控件显示的值
        /// </summary>
        public float Value
        {
            get { return number; }
            set
            {
                number = value;
                ///根据数值调制显示字符
                if (number > 10000 || number < -10000)
                {
                    dataStr = String.Format("{0:G}", number);
                    if (!dataStr.Contains("."))
                    {
                        switch (precision)
                        {
                            case 1:
                                dataStr += ".0";
                                break;
                            case 2:
                                dataStr += ".00";
                                break;
                        }
                    }
                }
                else
                    dataStr = String.Format(formatStr, number);
                length = dataStr.Length;
                if (dataStr.Contains("+"))
                {
                    String[] dataStrs = dataStr.Split('+');
                    dataStr = dataStrs[0] + dataStrs[1];
                    length -= 1;
                }
                changeSize();
            }
        }

        /// <summary>
        /// 设置控件显示数字的精度
        /// 提供最多两位小数
        /// </summary>
        public int Precision
        {
            get { return precision; }
            set
            {
                precision = value;
                if (precision > 2)
                    precision = 2;
                else if (precision < 0)
                    precision = 0;
                switch (precision)
                {
                    case 0:
                        formatStr = "{0:0}";
                        break;
                    case 1:
                        formatStr = "{0:0.0}";
                        break;
                    case 2:
                        formatStr = "{0:0.00}";
                        break;
                }
                Value = number;
            }
        }
        /// <summary>
        /// 控件显示数字的颜色
        /// </summary>
        public Color DialColor
        {
            get { return dialColor; }
            set
            {
                dialColor = value;
                this.Refresh();
            }
        }
        /// <summary>
        /// 公开设置控件显示值方法
        /// </summary>
        /// <param name="value"></param>
        public void setValue(float value)
        {
            Value = value;
        }

        /// <summary>
        /// 默认构造方法
        /// </summary>
        public DigitNumber()
        {
            InitializeComponent();
        }

        #region Overriden Control methods
        /// <summary>
        /// Draws the pointer.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            RectangleF digiFRect = new RectangleF(0 - styleCompensate, 0 - styleCompensate/2, this.Width, this.Height);
            Graphics graphics = e.Graphics;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            DisplayNumber(e.Graphics, this.number, digiFRect);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x20;
                return cp;
            }
        }
        #endregion

        /// <summary>
        /// Displays the given number in the 7-Segement format.
        /// </summary>
        /// <param name="g"></param>
        /// <param name="number"></param>
        /// <param name="drect"></param>
        private void DisplayNumber(Graphics g, float number, RectangleF rect)
        {
            try
            {
                dataStr.PadLeft(3, '0');
                char[] chars = dataStr.ToCharArray();
                int indexOfPoint = dataStr.IndexOf(".");
                for (int i = 0; i < chars.Length; i++)
                {
                    char c = chars[i];
                    if (c == '-')
                    {
                        DrawDigit(g, -1, new PointF(rect.X, rect.Y), false, rect.Height);
                    }
                    else if (c == '.')
                    {
                        DrawDigit(g, 10, new PointF(rect.X + width * i, rect.Y), true, rect.Height);
                    }
                    else if (c == 'E')
                    {
                        DrawDigit(g, 15, new PointF(rect.X + width * i - width * 0.6F, rect.Y), false, rect.Height);
                    }
                    else
                    {
                        if (indexOfPoint >= 0)
                        {
                            if (i < indexOfPoint)
                                DrawDigit(g, Int16.Parse(c.ToString()), new PointF(rect.X + width * i, rect.Y), false, rect.Height);
                            else
                                DrawDigit(g, Int16.Parse(c.ToString()), new PointF(rect.X + width * i - width * 0.6F, rect.Y), false, rect.Height);
                        }
                        else
                        {
                            DrawDigit(g, Int16.Parse(c.ToString()), new PointF(rect.X + width * i, rect.Y), false, rect.Height);
                        }
                    }
                }
            }
            catch (Exception)
            {

            }
        }

        /// <summary>
        /// Draws a digit in 7-Segement format.
        /// </summary>
        /// <param name="g"></param>
        /// <param name="number"></param>
        /// <param name="position"></param>
        /// <param name="dp"></param>
        /// <param name="height"></param>
        private void DrawDigit(Graphics g, int number, PointF position, bool point, float height)
        {
            Pen outline = new Pen(Color.FromArgb(40,Color.Gray));
            Pen fillPen = new Pen(this.dialColor);

            if (point)
            {
                //Draw decimal point
                g.FillEllipse(fillPen.Brush, new RectangleF(
                    position.X + GetX(1.5F, width),
                    position.Y + GetY(12F, this.Height),
                    width / 8,
                    width / 8));
            }
            else
            {
                #region Form Polygon Points
                //Segment A
                PointF[] segmentA = new PointF[5];
                segmentA[0] = segmentA[4] = new PointF(position.X + GetX(1.5F, width), position.Y + GetY(1F, height));
                segmentA[1] = new PointF(position.X + GetX(8.5F, width), position.Y + GetY(1F, height));
                segmentA[2] = new PointF(position.X + GetX(7.5F, width), position.Y + GetY(1.9F, height));
                segmentA[3] = new PointF(position.X + GetX(2.5F, width), position.Y + GetY(1.9F, height));

                //Segment B
                PointF[] segmentB = new PointF[5];
                segmentB[0] = segmentB[4] = new PointF(position.X + GetX(8.6F, width), position.Y + GetY(1.2F, height));
                segmentB[1] = new PointF(position.X + GetX(8.6F, width), position.Y + GetY(7.3F, height));
                segmentB[2] = new PointF(position.X + GetX(7.6F, width), position.Y + GetY(6.8F, height));
                segmentB[3] = new PointF(position.X + GetX(7.6F, width), position.Y + GetY(2.1F, height));

                //Segment C
                PointF[] segmentC = new PointF[5];
                segmentC[0] = segmentC[4] = new PointF(position.X + GetX(8.6F, width), position.Y + GetY(7.7F, height));
                segmentC[1] = new PointF(position.X + GetX(8.6F, width), position.Y + GetY(13.8F, height));
                segmentC[2] = new PointF(position.X + GetX(7.6F, width), position.Y + GetY(12.9F, height));
                segmentC[3] = new PointF(position.X + GetX(7.6F, width), position.Y + GetY(8.2F, height));

                //Segment D
                PointF[] segmentD = new PointF[5];
                segmentD[0] = segmentD[4] = new PointF(position.X + GetX(7.5F, width), position.Y + GetY(13.1F, height));
                segmentD[1] = new PointF(position.X + GetX(8.5F, width), position.Y + GetY(14F, height));
                segmentD[2] = new PointF(position.X + GetX(1.5F, width), position.Y + GetY(14F, height));
                segmentD[3] = new PointF(position.X + GetX(2.5F, width), position.Y + GetY(13.1F, height));

                //Segment E
                PointF[] segmentE = new PointF[5];
                segmentE[0] = segmentE[4] = new PointF(position.X + GetX(2.4F, width), position.Y + GetY(12.9F, height));
                segmentE[1] = new PointF(position.X + GetX(1.4F, width), position.Y + GetY(13.8F, height));
                segmentE[2] = new PointF(position.X + GetX(1.4F, width), position.Y + GetY(7.7F, height));
                segmentE[3] = new PointF(position.X + GetX(2.4F, width), position.Y + GetY(8.2F, height));

                //Segment F
                PointF[] segmentF = new PointF[5];
                segmentF[0] = segmentF[4] = new PointF(position.X + GetX(2.4F, width), position.Y + GetY(6.8F, height));
                segmentF[1] = new PointF(position.X + GetX(1.4F, width), position.Y + GetY(7.3F, height));
                segmentF[2] = new PointF(position.X + GetX(1.4F, width), position.Y + GetY(1.2F, height));
                segmentF[3] = new PointF(position.X + GetX(2.4F, width), position.Y + GetY(2.1F, height));

                //Segment G
                PointF[] segmentG = new PointF[7];
                segmentG[0] = segmentG[6] = new PointF(position.X + GetX(1.5F, width), position.Y + GetY(7.5F, height));
                segmentG[1] = new PointF(position.X + GetX(2.5F, width), position.Y + GetY(7.0F, height));
                segmentG[2] = new PointF(position.X + GetX(7.5F, width), position.Y + GetY(7.0F, height));
                segmentG[3] = new PointF(position.X + GetX(8.5F, width), position.Y + GetY(7.5F, height));
                segmentG[4] = new PointF(position.X + GetX(7.5F, width), position.Y + GetY(8.0F, height));
                segmentG[5] = new PointF(position.X + GetX(2.5F, width), position.Y + GetY(8.0F, height));

                //Segment DP
                #endregion

                #region Draw Segments Outline
                g.FillPolygon(outline.Brush, segmentA);
                g.FillPolygon(outline.Brush, segmentB);
                g.FillPolygon(outline.Brush, segmentC);
                g.FillPolygon(outline.Brush, segmentD);
                g.FillPolygon(outline.Brush, segmentE);
                g.FillPolygon(outline.Brush, segmentF);
                g.FillPolygon(outline.Brush, segmentG);
                #endregion

                #region Fill Segments
                //Fill SegmentA
                if (IsNumberAvailable(number, 0, 2, 3, 5, 6, 7, 8, 9,15))
                {
                    g.FillPolygon(fillPen.Brush, segmentA);
                }

                //Fill SegmentB
                if (IsNumberAvailable(number, 0, 1, 2, 3, 4, 7, 8, 9))
                {
                    g.FillPolygon(fillPen.Brush, segmentB);
                }

                //Fill SegmentC
                if (IsNumberAvailable(number, 0, 1, 3, 4, 5, 6, 7, 8, 9))
                {
                    g.FillPolygon(fillPen.Brush, segmentC);
                }

                //Fill SegmentD
                if (IsNumberAvailable(number, 0, 2, 3, 5, 6, 8, 9, 15))
                {
                    g.FillPolygon(fillPen.Brush, segmentD);
                }

                //Fill SegmentE
                if (IsNumberAvailable(number, 0, 2, 6, 8, 15))
                {
                    g.FillPolygon(fillPen.Brush, segmentE);
                }

                //Fill SegmentF
                if (IsNumberAvailable(number, 0, 4, 5, 6, 7, 8, 9, 15))
                {
                    g.FillPolygon(fillPen.Brush, segmentF);
                }

                //Fill SegmentG
                if (IsNumberAvailable(number, 2, 3, 4, 5, 6, 8, 9, -1, 15))
                {
                    g.FillPolygon(fillPen.Brush, segmentG);
                }
                #endregion
            }
        }

        /// <summary>
        /// Gets Relative X for the given width to draw digit
        /// </summary>
        /// <param name="x"></param>
        /// <param name="width"></param>
        /// <returns></returns>
        private float GetX(float x, float width)
        {
            return x * width / 10;
        }

        /// <summary>
        /// Gets relative Y for the given height to draw digit
        /// </summary>
        /// <param name="y"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        private float GetY(float y, float height)
        {
            return y * height / 15;
        }

        /// <summary>
        /// Returns true if a given number is available in the given list.
        /// </summary>
        /// <param name="number"></param>
        /// <param name="listOfNumbers"></param>
        /// <returns></returns>
        private bool IsNumberAvailable(int number, params int[] listOfNumbers)
        {
            if (listOfNumbers.Length > 0)
            {
                foreach (int i in listOfNumbers)
                {
                    if (i == number)
                        return true;
                }
            }
            return false;
        }
        /// <summary>
        /// 控件样式改变事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DigitNumber_StyleChanged(object sender, EventArgs e)
        {
            if (this.BorderStyle != borderStyle)
            {
                switch (this.BorderStyle)
                {
                    case BorderStyle.None:
                        styleCompensate = 0;
                        break;
                    case BorderStyle.Fixed3D:case BorderStyle.FixedSingle:
                        styleCompensate = width / 12;
                        break;
                }
                if (this.width < 150)
                    styleCompensate = 2;
                borderStyle = this.BorderStyle;
            }
        }
        /// <summary>
        /// 控件大小改变事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DigitNumber_Resize(object sender, EventArgs e)
        {
            if (this.Size != preSize)
            {
                changeSize();
            }
        }

        private void DigitNumber_SizeChanged(object sender, EventArgs e)
        {
            
        }

        /// <summary>
        /// 控件大小改变，调制控件尺寸，控制显示比例
        /// </summary>
        private void changeSize()
        {
            if (dataStr.Contains("."))
            {
                width = (int)(this.Height / 1.618);
                this.Width = width * (length - 1) + (int)(width * 2 / 5);
            }
            else
            {
                width = (int)(this.Height / 1.618);
                this.Width = width * length;
            }
            preSize = new Size(this.Width, this.Height);
            this.Refresh();
        }
    }
}
